﻿/** @file		ByteFIFO.c
	@author		Zerdligham
	@version	1.0
	@date		12/10/2012
	
	Ce fichier fourni les fonctions de base pour maintenir une file d'octets dont la taille maximale est fixée (l'implémentation repose sur un tableau). Par file, on entend une liste d'octets, réglé en mode "first in first out" (FIFO). Les octets sont lus dans le même ordre qu'ils sont ajoutés. C'est par exemple le comportement naturel d'un tampon.
	Le système ne prévois pas en lui-même une protection contre les interruptions. La meilleure méthode est a priori de protéger toutes les fonctions d'ajout ou de lecture de données.
*/	

#include <stdlib.h>
#include <string.h>
#include "typedef.h"
#include "ByteFIFO.h"

/**
 * Crée une nouvelle FIFO.
 * @param size  taille maximale des données à stoquer (en octets)
 * @return      un pointeur vers cette FIFO
 * @warning     Il s'agit en arrière plan d'un malloc, il faut donc libérer la mémoire avec ByteFIFO_free.
 */
inline ByteFIFO ByteFIFO_new(const U16 size) {
    ByteFIFO ret = malloc(sizeof(struct ByteFIFO_struct) + size * sizeof(U8));
    if (ret != null) {
        ret->size = size;
        ret->readPtr = 0;
        ret->writePtr = 0;
		ret->dataSize = 0;
    }
    return ret;
}

/**
 * Teste si la FIFO contient des données
 * @param fifo  FIFO à tester
 * @return      vrai si et seulement si la FIFO est vide
 */
inline U8 ByteFIFO_isEmpty(const ByteFIFO fifo) {
    return fifo->dataSize == 0;
}

/**
 * Teste si la FIFO est pleine
 * @param fifo  FIFO à tester
 * @return      vrai si et seulement si la FIFO est pleine
 */
inline U8 ByteFIFO_isFull(const ByteFIFO fifo) {
    return fifo->dataSize == fifo->size;
}

/**
 * Détermine le nombre d'octets de données stockées dans la FIFO
 * @param fifo  FIFO à tester
 * @return      Nombre d'octets de données
 */
inline U16 ByteFIFO_getDataSize(const ByteFIFO fifo) {
    return fifo->dataSize;
}

/**
 * Détermine le nombre d'octets libres dans la FIFO
 * @param fifo  FIFO à tester
 * @return      Nombre d'octets libres
 */
inline U16 ByteFIFO_getAvailableSize(const ByteFIFO fifo) {
    return fifo->size - fifo->dataSize;
}

/**
 * Vide la FIFO
 * @param fifo  FIFO à vider
 * @warning		La fonction ne libère pas la mémoire associée. Pour ça, il faut utiliser ByteFIFO_free
 */
inline void ByteFIFO_clear(ByteFIFO fifo) {
    fifo->readPtr = fifo->writePtr = 0;
	fifo->dataSize = 0;
}

/**
 * Libère la mémoire de la FIFO
 * @param fifo  FIFO à supprimer
 * @warning		La fonction libère la mémoire associée. Il faut donc l'utiliser comme un free : ne pas l'éxécuter sur une FIFO non initialisée, et ne pas essayer d'utiliser la FIFO aprè-s en avoir libéré la mémoire
 */
inline void ByteFIFO_free(ByteFIFO fifo) {
	free(fifo);
}

/**
 * Renvoie la valeur du premier octet de la FIFO. Contrairement à ByteFIFO_pop, la donnée n'est pas retirée de la FIFO.
 * @param fifo  FIFO à lire
 * @return      Octet lu
 * @warning	La valeur renvoyée ne permet pas de savoir si la FIFO était vide. Il faut le tester avant d'exécuter cette fonction. Dans le cas où on l'exécute alors que la FIFO est vide, la fonction renvoie 0.
 */
inline U8 ByteFIFO_get(const ByteFIFO fifo) {
	if(!ByteFIFO_isEmpty(fifo)) {
		return fifo->data[fifo->readPtr];
	} else {
		return 0;
	}
}

/**
 * Renvoie la valeur du premier octet de la FIFO. Contrairement à ByteFIFO_get, la donnée est retirée de la FIFO.
 * @param fifo  FIFO à lire
 * @return      Octet lu
 * @warning	La valeur renvoyée ne permet pas de savoir si la FIFO était vide. Il faut le tester avant d'exécuter cette fonction. Dans le cas où on l'exécute alors que la FIFO est vide, la fonction renvoie 0. Par contre un test est fait afin de ne pas "supprimer" une donnée inexistante. On ne risque donc pas de corrompre la FIFO.
 */
inline U8 ByteFIFO_pop(ByteFIFO fifo) {
	if(!ByteFIFO_isEmpty(fifo)) {
		U8 ret = fifo->data[fifo->readPtr++];
		if(fifo->readPtr == fifo->size) {
			fifo->readPtr=0;	
		}
		fifo->dataSize--;
		return ret;
	} else {
		return 0;
	}
}

/**
 * Ecrit un octet dans la FIFO.
 * @param fifo  FIFO dans laquelle écrire
 * @param data	Octet a àjouter
 * @return      ByteFIFO_ok si l'écriture s'est déroulée comme prévu, ByteFIFO_full si elle était pleine.
 */
inline ByteFIFO_Error ByteFIFO_push(ByteFIFO fifo, const U8 data) {
	if(!ByteFIFO_isFull(fifo)) {
		fifo->data[fifo->writePtr++] = data;
		if(fifo->writePtr == fifo->size) {
			fifo->writePtr=0;
		}
		fifo->dataSize ++;
		return ByteFIFO_ok;
	} else {
        return ByteFIFO_full;
    }
}

/**
 * Ecrit plusieurs octets dans la FIFO.
 * @param fifo  FIFO dans laquelle écrire
 * @param size	Nombre d'octets à écrire
 * @param data	pointeur vers les données à copier
 * @return      ByteFIFO_ok si l'écriture s'est déroulée comme prévu, ByteFIFO_full si la place disponible n'était pas suffisante. Dans ce cas, aucun octet n'est copié.
 */
inline ByteFIFO_Error ByteFIFO_pushBlock(ByteFIFO fifo, const U16 size, const void* data) {
    if (ByteFIFO_getAvailableSize(fifo) >= size) {
        U8* data_ = (U8*) data;
        if (fifo->writePtr + size >= fifo->size) { // writeptr se doit faire un modulo (qu'on doive ou non écrire au début du tableau)
            memcpy(fifo->data + fifo->writePtr, data_, fifo->size - fifo->writePtr);
            memcpy(fifo->data, data_ + fifo->size - fifo->writePtr, size + fifo->writePtr - fifo->size);
            fifo->writePtr = size + fifo->writePtr - fifo->size;
        } else {
            memcpy(fifo->data + fifo->writePtr, data_, size);
            fifo->writePtr += size;
        }
		fifo->dataSize += size;
        return ByteFIFO_ok;
    } else {
        return ByteFIFO_full;
    }
}

/**
 * Lit plusieurs octets de la FIFO.
 * @param fifo  FIFO à lire
 * @param size	Nombre d'octets à lire
 * @param data	pointeur vers le buffer dans lequel les données lues sont copiées
 * @return      ByteFIFO_ok si la lecture s'est déroulée comme prévu, ByteFIFO_notEnoughtData si on a essayé de lire plus de données qu'il y en avait disponibles. Dans ce cas, aucun octet n'est lu.
 */
inline ByteFIFO_Error ByteFIFO_popBlock(ByteFIFO fifo, const U16 size, void* data) {
    if (ByteFIFO_getDataSize(fifo) >= size) {
        U8* data_ = (U8*) data;
        if (fifo->readPtr + size >= fifo->size) {
            memcpy(data_, fifo->data + fifo->readPtr, fifo->size - fifo->readPtr);
            memcpy(data_ + fifo->size - fifo->readPtr, fifo->data, size + fifo->readPtr - fifo->size);
            fifo->readPtr = size + fifo->readPtr - fifo->size;
        } else {
            memcpy(data_, fifo->data + fifo->readPtr, size);
            fifo->readPtr += size;
        }
		fifo->dataSize -= size;
        return ByteFIFO_ok;
    } else {
        return ByteFIFO_notEnoughtData;
    }
}
